<?php
/**
 * 文件字段的值支持如下
 * 字符串 temp::xxx.file
 * 一维数组 多个文件 ["temp::xxx.file","temp::yyy.file"]，单个文件 ["path"=>"temp::xxx.file","name"=>"xxx"]
 * 二维数组 [["path"=>"temp::xxx.file","name"=>"xxx"],["path"=>"temp::yyy.file","name"=>"yyy"]]
 * 需要支持数据值里 ，需在 model的 $casts 中 将文件字段设置为 json 或 array
 */

namespace Hilaoyu\LaravelExtend\Services\Files\Traits;

use Hilaoyu\LaravelExtend\Services\Files\AbstractModelFileUse;
use Hilaoyu\LaravelExtend\Services\Files\FileUseVirtualModel;
use Hilaoyu\Utils\UtilFile;
use Hilaoyu\Utils\UtilSupport;
use  Hilaoyu\LaravelExtend\Services\Files\FileService;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

trait ModelWithFilesTrait
{


    //设置普通文件字段 ['字段名'=>'存储区名']
    protected function modelWithFilesTraitSettingFileFields(): array
    {
        return [];
    }

    //设置图片文件字段 ['字段名'=>'存储区名']
    protected function modelWithFilesTraitSettingImageFields(): array
    {
        return [];
    }

    //设置需要清洗内容文件的字段 ['字段名'=>'存储区名']
    protected function modelWithFilesTraitSettingContentHasFileFields(): array
    {
        return [];
    }

    //设置需要加密存储的字段 ['字段名'=>'密钥 base64'] 密钥为空时则自动生成
    protected function modelWithFilesTraitSettingEncryptFields(): array
    {
        return [];
    }

    private function modelWithFilesTraitMergeFileFields()
    {
        return array_merge([], $this->modelWithFilesTraitSettingFileFields(), $this->modelWithFilesTraitSettingImageFields());
    }

    public function fileUploaders(){
        $fileds = $this->modelWithFilesTraitMergeFileFields();
        $uploaders = [];
        foreach ($fileds as $filed => $disk){
            $uploaders[$filed] = UtilSupport::tryQuiet(function () use ($disk){
                return route('api.l-e-s.files.uploader',['disk'=>$disk]);
            });
        }

        return $uploaders;
    }

    private function modelWithFilesTraitGetAppends()
    {
        $appends = [];
        foreach (array_keys($this->modelWithFilesTraitMergeFileFields()) as $field_name) {
            $appends[] = $field_name . '_upload_disk';
        }
        return $appends;
    }

    public function eventOnConstructModelWithFilesTrait()
    {
        $this->append($this->modelWithFilesTraitGetAppends());
    }

    public function eventOnRetrievedModelWithFilesTrait()
    {
        $this->append($this->modelWithFilesTraitGetAppends());
    }


    /*public function eventOnSavingModelWithFilesTrait()
    {

        $file_fields = $this->modelWithFilesTraitMergeFileFields();


        //dump($this);

        //dd($file_fields);


        foreach ($file_fields as $field_name => $fs_disk) {
            $value = data_get($this->attributes, $field_name);
            //$value = $this->getAttribute($field_name);


            $org_value = $this->getOriginal($field_name);
            if ($this->hasCast($field_name)) {
                $value = $this->castAttribute($field_name, $value);
                $org_value = $this->castAttribute($field_name, $org_value);
            }

            $new_url = [];
            if (is_string($value)) {
                $new_url[] = $value;
            }elseif(is_array($value)){
                foreach ($value as $k=>$v) {
                    if (is_string($v)) {
                        $new_url[] = $v;
                    } elseif (is_array($v)) {
                        if($url = data_get($v,'file','')){
                            $new_url[] = $url;
                        }
                    }
                }
            }

            if(is_string($org_value)){
                if(!in_array($org_value,$new_url)){
                    UtilSupport::tryQuiet(function () use ($org_value){
                        FileService::make($org_value)->delete();
                    });
                }
            }elseif (is_array($org_value)){
                foreach ($org_value as $k=>$v) {

                    if (is_string($v)) {
                        if(!in_array($v,$new_url)){
                            UtilSupport::tryQuiet(function () use ($v){
                                FileService::make($v)->delete();
                            });
                        }
                    } elseif (is_array($v)) {
                        if(array_key_exists('file',$v)){
                            if(!in_array(data_get($v,'file',''),$new_url)){
                                UtilSupport::tryQuiet(function () use ($v){
                                    FileService::make(data_get($v,'file',''))->delete();
                                });
                            }
                        }


                    }
                }
            }
        }

        foreach ($this->modelWithFilesTraitSettingContentHasFileFields() as $key => $fs_disk) {
            $value = data_get($this->attributes, $field_name);
            //$value = $this->getAttribute($field_name);


            $org_value = $this->getOriginal($field_name);
            if ($this->hasCast($field_name)) {
                $value = $this->castAttribute($field_name, $value);
                $org_value = $this->castAttribute($field_name, $org_value);
            }
            if(is_string($org_value)){
                $org_urls = FileService::contentFiles($org_value);

            }elseif (is_array($org_value)){
                $org_urls = [];
                foreach ($org_value as $ov){
                    $org_urls_temp = FileService::contentFiles($ov);
                    $org_urls = array_merge($org_urls,$org_urls_temp);
                }
            }
            //dump($key,$value);
            if (is_string($value)) {
                $new_urls = FileService::contentFiles($value);

            }elseif (is_array($value)){
                $new_urls = [];
                foreach ($value as $cv){
                    $new_urls_temp = FileService::contentFiles($cv);
                    $new_urls = array_merge($new_urls,$new_urls_temp);
                }
            }

            foreach ($org_urls as $url){
                $find = Arr::where($new_urls, function ($uv, $uk) use($url){
                    return $uv == $url;
                });
                if(empty($find)){
                    UtilSupport::tryQuiet(function () use ($url){
                        FileService::make($url)->delete();
                    });
                }
            }
            //$this->attributes[$key] = $value;
        }

    }
    */

    public function eventOnSavedModelWithFilesTrait()
    {
        $this->modelWithFilesTraitUseFile();
    }

    public function modelWithFilesTraitUseFile()
    {

        $file_fields = $this->modelWithFilesTraitMergeFileFields();


        //dump($this);

        //dd($file_fields);


        foreach ($file_fields as $field_name => $fs_disk) {
            $value = data_get($this->attributes, $field_name);
            //$value = $this->getAttribute($field_name);
            if ($this->hasCast($field_name)) {
                $value = $this->castAttribute($field_name, $value);
            }


            if (is_string($value)) {
                if ($fs = $this->modelWithFilesTraitFSUseFile($value, $field_name)) {
                    //
                }

            } elseif (is_array($value)) {
                if (array_key_exists('file', $value)) {
                    if ($fs = $this->modelWithFilesTraitFSUseFile((string)data_get($value, 'file', ''), $field_name)) {
                        //
                    }
                } else {
                    foreach ($value as $k => $v) {
                        if (is_string($v)) {
                            if ($fs = $this->modelWithFilesTraitFSUseFile($v, $field_name)) {
                                //
                            }
                        } elseif (is_array($v)) {
                            if (array_key_exists('file', $v)) {
                                if ($fs = $this->modelWithFilesTraitFSUseFile(data_get($v, 'file', ''), $field_name)) {
                                    //
                                }
                            }

                        }
                    }
                }

            }


        }

        foreach ($this->modelWithFilesTraitSettingContentHasFileFields() as $key => $fs_disk) {
            $value = $this->getAttribute($key);
            //dump($key,$value);

            if (is_string($value)) {
                $value = FileService::contentFsUseFor($value);
            } elseif (is_array($value)) {
                foreach ($value as $ck => $cv) {
                    $value = FileService::contentFsUseFor($cv);
                }
            }

            //$this->attributes[$key] = $value;
        }


    }


    private function modelWithFilesTraitFSUseFile($value, $field_name)
    {
        if (!$field_name || !$value) {
            return null;
        }
        $encrypt_fields = $this->modelWithFilesTraitSettingEncryptFields();

        $FS = FileService::make($value);
        if (!$FS) {
            return null;
        }
        $secret = null;
        if (array_key_exists($field_name, $encrypt_fields)) {
            $secret = UtilSupport::tryQuiet(function () use ($encrypt_fields,$field_name) {
                return base64_decode(data_get($encrypt_fields, $field_name, ''));
            });
            if(!$secret){
                $secret = FileService::generateSecret();
            }

        } else {

        }

        $FS = $FS->queueUseFor($secret);

        return $FS;
    }


    /*public function eventOnDeletedModelWithFilesTrait()
    {
        //dump($this);
        foreach ($file_fields as $field_name => $fs_disk) {
            $value = data_get($this->attributes, $field_name);
            if ($this->hasCast($field_name)) {
                $value = $this->castAttribute($field_name, $value);
            }
            //dump($key,$value);
            if (is_string($value)) {
                UtilSupport::tryQuiet(function () use ($value){
                    FileService::make($value)->delete();
                });


            } elseif (is_array($value)) {
                if(array_key_exists('file',$value)){
                    UtilSupport::tryQuiet(function () use ($value){
                        FileService::make(data_get((string)$value,'file',''))->delete();
                    });
                }else{
                    foreach ($value as $k=>$v) {
                        if (is_string($v)) {
                            UtilSupport::tryQuiet(function () use ($v){
                                FileService::make($v)->delete();
                            });
                        } elseif (is_array($v)) {
                            if(array_key_exists('file',$v)){
                                UtilSupport::tryQuiet(function () use ($v){
                                    FileService::make(data_get($v,'file',''))->delete();
                                });
                            }

                        }
                    }
                }

            }

        }

        foreach ($this->modelWithFilesTraitSettingContentHasFileFields() as $key => $fs_disk) {
            $value = $this->getAttribute($key);
            //dump($key,$value);
            if (is_string($value)) {
                $value = FileService::contentFsDelete($value);
            }elseif (is_array($value)){
                foreach ($value as $ck => $cv){
                    $value = FileService::contentFsDelete($cv);
                }
            }

        }

        //dd($this);
    }*/


    private function modelWithFilesTraitGetFileValue($field_name)
    {
        if (Str::endsWith($field_name,'_upload_disk')) {
            $field_key = Str::beforeLast($field_name, '_upload_disk');
            $file_fields = $this->modelWithFilesTraitMergeFileFields();
            return data_get($file_fields, $field_key, '');
        }


        $value = data_get($this->attributes, $field_name);
        //$value = $this->getAttribute($field_name);
        if ($this->hasCast($field_name)) {
            $value = $this->castAttribute($field_name, $value);
        }


        /*if (is_string($value)) {
            if($files_model = UtilSupport::tryQuiet(function () use ($value){
                return FileService::make($value)->filesModel();
            })){
                $value = $files_model;
            }

        } elseif (is_array($value)) {
            if(array_key_exists('file',$value)){
                if($files_model = UtilSupport::tryQuiet(function () use ($value){
                    return FileService::make((string)data_get($value,'file',''))->filesModel();
                })){
                    $value['file'] = $files_model;
                }
            }else{
                foreach ($value as $k=>$v) {
                    if (is_string($v)) {
                        if($files_model = UtilSupport::tryQuiet(function () use ($v){
                            return FileService::make($v)->filesModel();
                        })){
                            $value[$k] = $files_model;
                        }
                    } elseif (is_array($v)) {
                        if(array_key_exists('file',$v)){
                            if($files_model = UtilSupport::tryQuiet(function () use ($v){
                                return FileService::make((string)data_get($v,'file',''))->filesModel();
                            })){
                                $v['file'] = $files_model;
                            }
                        }
                        $value[$k] = $v;
                    }
                }
            }

        }*/


        return $value;


    }


    /*public function hasGetMutator($key)
    {
        if (in_array($key, array_keys($this->modelWithFilesTraitMergeFileFields()))) {
            //dump('has');
            return true;
        }
        return parent::hasGetMutator($key);
    }*/


    public function __call($method, $parameters)
    {

        if ($field_name = $this->modelWithFilesTraitMethodToAttributeKey($method)) {
            return $this->modelWithFilesTraitGetFileValue($field_name);
        }


        return parent::__call($method, $parameters); // TODO: Change the autogenerated stub
    }


    private function modelWithFilesTraitMethodToAttributeKey($method)
    {
        $method = trim($method);
        if (Str::startsWith($method, 'get') && Str::endsWith($method, 'Attribute')) {
            $file_field = Str::snake(Str::substr(Str::substr($method, 0, Str::length($method) - 9), 3));

            if (in_array($file_field, $this->modelWithFilesTraitGetAppends())) {
                return $file_field;
            }
        }
        return '';
    }


}
